home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkMessage.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  25.4 KB  |  849 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkMessage.c --
  3.  *
  4.  *    This module implements a message widgets for the Tk
  5.  *    toolkit.  A message widget displays a multi-line string
  6.  *    in a window according to a particular aspect ratio.
  7.  *
  8.  * Copyright (c) 1990-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * SCCS: @(#) tkMessage.c 1.75 97/07/31 09:11:14
  15.  */
  16.  
  17. #include "tkPort.h"
  18. #include "default.h"
  19. #include "tkInt.h"
  20.  
  21. /*
  22.  * A data structure of the following type is kept for each message
  23.  * widget managed by this file:
  24.  */
  25.  
  26. typedef struct {
  27.     Tk_Window tkwin;        /* Window that embodies the message.  NULL
  28.                  * means that the window has been destroyed
  29.                  * but the data structures haven't yet been
  30.                  * cleaned up.*/
  31.     Display *display;        /* Display containing widget.  Used, among
  32.                  * other things, so that resources can be
  33.                  * freed even after tkwin has gone away. */
  34.     Tcl_Interp *interp;        /* Interpreter associated with message. */
  35.     Tcl_Command widgetCmd;    /* Token for message's widget command. */
  36.  
  37.     /*
  38.      * Information used when displaying widget:
  39.      */
  40.  
  41.     char *string;        /* String displayed in message. */
  42.     int numChars;        /* Number of characters in string, not
  43.                  * including terminating NULL character. */
  44.     char *textVarName;        /* Name of variable (malloc'ed) or NULL.
  45.                  * If non-NULL, message displays the contents
  46.                  * of this variable. */
  47.     Tk_3DBorder border;        /* Structure used to draw 3-D border and
  48.                  * background.  NULL means a border hasn't
  49.                  * been created yet. */
  50.     int borderWidth;        /* Width of border. */
  51.     int relief;            /* 3-D effect: TK_RELIEF_RAISED, etc. */
  52.     int highlightWidth;        /* Width in pixels of highlight to draw
  53.                  * around widget when it has the focus.
  54.                  * <= 0 means don't draw a highlight. */
  55.     XColor *highlightBgColorPtr;
  56.                 /* Color for drawing traversal highlight
  57.                  * area when highlight is off. */
  58.     XColor *highlightColorPtr;    /* Color for drawing traversal highlight. */
  59.     Tk_Font tkfont;        /* Information about text font, or NULL. */
  60.     XColor *fgColorPtr;        /* Foreground color in normal mode. */
  61.     int padX, padY;        /* User-requested extra space around text. */
  62.     int width;            /* User-requested width, in pixels.  0 means
  63.                  * compute width using aspect ratio below. */
  64.     int aspect;            /* Desired aspect ratio for window
  65.                  * (100*width/height). */
  66.     int msgWidth;        /* Width in pixels needed to display
  67.                  * message. */
  68.     int msgHeight;        /* Height in pixels needed to display
  69.                  * message. */
  70.     Tk_Anchor anchor;        /* Where to position text within window region
  71.                  * if window is larger or smaller than
  72.                  * needed. */
  73.     Tk_Justify justify;        /* Justification for text. */
  74.  
  75.     GC textGC;            /* GC for drawing text in normal mode. */
  76.     Tk_TextLayout textLayout;    /* Saved layout information. */
  77.  
  78.     /*
  79.      * Miscellaneous information:
  80.      */
  81.  
  82.     Tk_Cursor cursor;        /* Current cursor for window, or None. */
  83.     char *takeFocus;        /* Value of -takefocus option;  not used in
  84.                  * the C code, but used by keyboard traversal
  85.                  * scripts.  Malloc'ed, but may be NULL. */
  86.     int flags;            /* Various flags;  see below for
  87.                  * definitions. */
  88. } Message;
  89.  
  90. /*
  91.  * Flag bits for messages:
  92.  *
  93.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  94.  *                has already been queued to redraw
  95.  *                this window.
  96.  * GOT_FOCUS:            Non-zero means this button currently
  97.  *                has the input focus.
  98.  */
  99.  
  100. #define REDRAW_PENDING        1
  101. #define GOT_FOCUS        4
  102.  
  103. /*
  104.  * Information used for argv parsing.
  105.  */
  106.  
  107. static Tk_ConfigSpec configSpecs[] = {
  108.     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
  109.     DEF_MESSAGE_ANCHOR, Tk_Offset(Message, anchor), 0},
  110.     {TK_CONFIG_INT, "-aspect", "aspect", "Aspect",
  111.     DEF_MESSAGE_ASPECT, Tk_Offset(Message, aspect), 0},
  112.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  113.     DEF_MESSAGE_BG_COLOR, Tk_Offset(Message, border),
  114.     TK_CONFIG_COLOR_ONLY},
  115.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  116.     DEF_MESSAGE_BG_MONO, Tk_Offset(Message, border),
  117.     TK_CONFIG_MONO_ONLY},
  118.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  119.     (char *) NULL, 0, 0},
  120.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  121.     (char *) NULL, 0, 0},
  122.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  123.     DEF_MESSAGE_BORDER_WIDTH, Tk_Offset(Message, borderWidth), 0},
  124.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  125.     DEF_MESSAGE_CURSOR, Tk_Offset(Message, cursor), TK_CONFIG_NULL_OK},
  126.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  127.     (char *) NULL, 0, 0},
  128.     {TK_CONFIG_FONT, "-font", "font", "Font",
  129.     DEF_MESSAGE_FONT, Tk_Offset(Message, tkfont), 0},
  130.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  131.     DEF_MESSAGE_FG, Tk_Offset(Message, fgColorPtr), 0},
  132.     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
  133.     "HighlightBackground", DEF_MESSAGE_HIGHLIGHT_BG,
  134.     Tk_Offset(Message, highlightBgColorPtr), 0},
  135.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  136.     DEF_MESSAGE_HIGHLIGHT, Tk_Offset(Message, highlightColorPtr), 0},
  137.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  138.     "HighlightThickness",
  139.     DEF_MESSAGE_HIGHLIGHT_WIDTH, Tk_Offset(Message, highlightWidth), 0},
  140.     {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
  141.     DEF_MESSAGE_JUSTIFY, Tk_Offset(Message, justify), 0},
  142.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  143.     DEF_MESSAGE_PADX, Tk_Offset(Message, padX), 0},
  144.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  145.     DEF_MESSAGE_PADY, Tk_Offset(Message, padY), 0},
  146.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  147.     DEF_MESSAGE_RELIEF, Tk_Offset(Message, relief), 0},
  148.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  149.     DEF_MESSAGE_TAKE_FOCUS, Tk_Offset(Message, takeFocus),
  150.     TK_CONFIG_NULL_OK},
  151.     {TK_CONFIG_STRING, "-text", "text", "Text",
  152.     DEF_MESSAGE_TEXT, Tk_Offset(Message, string), 0},
  153.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  154.     DEF_MESSAGE_TEXT_VARIABLE, Tk_Offset(Message, textVarName),
  155.     TK_CONFIG_NULL_OK},
  156.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  157.     DEF_MESSAGE_WIDTH, Tk_Offset(Message, width), 0},
  158.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  159.     (char *) NULL, 0, 0}
  160. };
  161.  
  162. /*
  163.  * Forward declarations for procedures defined later in this file:
  164.  */
  165.  
  166. static void        MessageCmdDeletedProc _ANSI_ARGS_((
  167.                 ClientData clientData));
  168. static void        MessageEventProc _ANSI_ARGS_((ClientData clientData,
  169.                 XEvent *eventPtr));
  170. static char *        MessageTextVarProc _ANSI_ARGS_((ClientData clientData,
  171.                 Tcl_Interp *interp, char *name1, char *name2,
  172.                 int flags));
  173. static int        MessageWidgetCmd _ANSI_ARGS_((ClientData clientData,
  174.                 Tcl_Interp *interp, int argc, char **argv));
  175. static void        MessageWorldChanged _ANSI_ARGS_((
  176.                 ClientData instanceData));
  177. static void        ComputeMessageGeometry _ANSI_ARGS_((Message *msgPtr));
  178. static int        ConfigureMessage _ANSI_ARGS_((Tcl_Interp *interp,
  179.                 Message *msgPtr, int argc, char **argv,
  180.                 int flags));
  181. static void        DestroyMessage _ANSI_ARGS_((char *memPtr));
  182. static void        DisplayMessage _ANSI_ARGS_((ClientData clientData));
  183.  
  184. /*
  185.  * The structure below defines message class behavior by means of procedures
  186.  * that can be invoked from generic window code.
  187.  */
  188.  
  189. static TkClassProcs messageClass = {
  190.     NULL,            /* createProc. */
  191.     MessageWorldChanged,    /* geometryProc. */
  192.     NULL            /* modalProc. */
  193. };
  194.  
  195.  
  196. /*
  197.  *--------------------------------------------------------------
  198.  *
  199.  * Tk_MessageCmd --
  200.  *
  201.  *    This procedure is invoked to process the "message" Tcl
  202.  *    command.  See the user documentation for details on what
  203.  *    it does.
  204.  *
  205.  * Results:
  206.  *    A standard Tcl result.
  207.  *
  208.  * Side effects:
  209.  *    See the user documentation.
  210.  *
  211.  *--------------------------------------------------------------
  212.  */
  213.  
  214. int
  215. Tk_MessageCmd(clientData, interp, argc, argv)
  216.     ClientData clientData;    /* Main window associated with
  217.                  * interpreter. */
  218.     Tcl_Interp *interp;        /* Current interpreter. */
  219.     int argc;            /* Number of arguments. */
  220.     char **argv;        /* Argument strings. */
  221. {
  222.     register Message *msgPtr;
  223.     Tk_Window new;
  224.     Tk_Window tkwin = (Tk_Window) clientData;
  225.  
  226.     if (argc < 2) {
  227.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  228.         argv[0], " pathName ?options?\"", (char *) NULL);
  229.     return TCL_ERROR;
  230.     }
  231.  
  232.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  233.     if (new == NULL) {
  234.     return TCL_ERROR;
  235.     }
  236.  
  237.     msgPtr = (Message *) ckalloc(sizeof(Message));
  238.     msgPtr->tkwin = new;
  239.     msgPtr->display = Tk_Display(new);
  240.     msgPtr->interp = interp;
  241.     msgPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(msgPtr->tkwin),
  242.         MessageWidgetCmd, (ClientData) msgPtr, MessageCmdDeletedProc);
  243.     msgPtr->textLayout = NULL;
  244.     msgPtr->string = NULL;
  245.     msgPtr->numChars = 0;
  246.     msgPtr->textVarName = NULL;
  247.     msgPtr->border = NULL;
  248.     msgPtr->borderWidth = 0;
  249.     msgPtr->relief = TK_RELIEF_FLAT;
  250.     msgPtr->highlightWidth = 0;
  251.     msgPtr->highlightBgColorPtr = NULL;
  252.     msgPtr->highlightColorPtr = NULL;
  253.     msgPtr->tkfont = NULL;
  254.     msgPtr->fgColorPtr = NULL;
  255.     msgPtr->textGC = None;
  256.     msgPtr->padX = 0;
  257.     msgPtr->padY = 0;
  258.     msgPtr->anchor = TK_ANCHOR_CENTER;
  259.     msgPtr->width = 0;
  260.     msgPtr->aspect = 150;
  261.     msgPtr->msgWidth = 0;
  262.     msgPtr->msgHeight = 0;
  263.     msgPtr->justify = TK_JUSTIFY_LEFT;
  264.     msgPtr->cursor = None;
  265.     msgPtr->takeFocus = NULL;
  266.     msgPtr->flags = 0;
  267.  
  268.     Tk_SetClass(msgPtr->tkwin, "Message");
  269.     TkSetClassProcs(msgPtr->tkwin, &messageClass, (ClientData) msgPtr);
  270.     Tk_CreateEventHandler(msgPtr->tkwin,
  271.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  272.         MessageEventProc, (ClientData) msgPtr);
  273.     if (ConfigureMessage(interp, msgPtr, argc-2, argv+2, 0) != TCL_OK) {
  274.     goto error;
  275.     }
  276.  
  277.     interp->result = Tk_PathName(msgPtr->tkwin);
  278.     return TCL_OK;
  279.  
  280.     error:
  281.     Tk_DestroyWindow(msgPtr->tkwin);
  282.     return TCL_ERROR;
  283. }
  284.  
  285. /*
  286.  *--------------------------------------------------------------
  287.  *
  288.  * MessageWidgetCmd --
  289.  *
  290.  *    This procedure is invoked to process the Tcl command
  291.  *    that corresponds to a widget managed by this module.
  292.  *    See the user documentation for details on what it does.
  293.  *
  294.  * Results:
  295.  *    A standard Tcl result.
  296.  *
  297.  * Side effects:
  298.  *    See the user documentation.
  299.  *
  300.  *--------------------------------------------------------------
  301.  */
  302.  
  303. static int
  304. MessageWidgetCmd(clientData, interp, argc, argv)
  305.     ClientData clientData;    /* Information about message widget. */
  306.     Tcl_Interp *interp;        /* Current interpreter. */
  307.     int argc;            /* Number of arguments. */
  308.     char **argv;        /* Argument strings. */
  309. {
  310.     register Message *msgPtr = (Message *) clientData;
  311.     size_t length;
  312.     int c;
  313.  
  314.     if (argc < 2) {
  315.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  316.         " option ?arg arg ...?\"", (char *) NULL);
  317.     return TCL_ERROR;
  318.     }
  319.     c = argv[1][0];
  320.     length = strlen(argv[1]);
  321.     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  322.         && (length >= 2)) {
  323.     if (argc != 3) {
  324.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  325.             argv[0], " cget option\"",
  326.             (char *) NULL);
  327.         return TCL_ERROR;
  328.     }
  329.     return Tk_ConfigureValue(interp, msgPtr->tkwin, configSpecs,
  330.         (char *) msgPtr, argv[2], 0);
  331.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  332.         && (length  >= 2)) {
  333.     if (argc == 2) {
  334.         return Tk_ConfigureInfo(interp, msgPtr->tkwin, configSpecs,
  335.             (char *) msgPtr, (char *) NULL, 0);
  336.     } else if (argc == 3) {
  337.         return Tk_ConfigureInfo(interp, msgPtr->tkwin, configSpecs,
  338.             (char *) msgPtr, argv[2], 0);
  339.     } else {
  340.         return ConfigureMessage(interp, msgPtr, argc-2, argv+2,
  341.             TK_CONFIG_ARGV_ONLY);
  342.     }
  343.     } else {
  344.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  345.         "\": must be cget or configure", (char *) NULL);
  346.     return TCL_ERROR;
  347.     }
  348. }
  349.  
  350. /*
  351.  *----------------------------------------------------------------------
  352.  *
  353.  * DestroyMessage --
  354.  *
  355.  *    This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
  356.  *    to clean up the internal structure of a message at a safe time
  357.  *    (when no-one is using it anymore).
  358.  *
  359.  * Results:
  360.  *    None.
  361.  *
  362.  * Side effects:
  363.  *    Everything associated with the message is freed up.
  364.  *
  365.  *----------------------------------------------------------------------
  366.  */
  367.  
  368. static void
  369. DestroyMessage(memPtr)
  370.     char *memPtr;        /* Info about message widget. */
  371. {
  372.     register Message *msgPtr = (Message *) memPtr;
  373.  
  374.     /*
  375.      * Free up all the stuff that requires special handling, then
  376.      * let Tk_FreeOptions handle all the standard option-related
  377.      * stuff.
  378.      */
  379.  
  380.     Tk_FreeTextLayout(msgPtr->textLayout);
  381.     if (msgPtr->textVarName != NULL) {
  382.     Tcl_UntraceVar(msgPtr->interp, msgPtr->textVarName,
  383.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  384.         MessageTextVarProc, (ClientData) msgPtr);
  385.     }
  386.     if (msgPtr->textGC != None) {
  387.     Tk_FreeGC(msgPtr->display, msgPtr->textGC);
  388.     }
  389.     Tk_FreeOptions(configSpecs, (char *) msgPtr, msgPtr->display, 0);
  390.     ckfree((char *) msgPtr);
  391. }
  392.  
  393. /*
  394.  *----------------------------------------------------------------------
  395.  *
  396.  * ConfigureMessage --
  397.  *
  398.  *    This procedure is called to process an argv/argc list, plus
  399.  *    the Tk option database, in order to configure (or
  400.  *    reconfigure) a message widget.
  401.  *
  402.  * Results:
  403.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  404.  *    returned, then interp->result contains an error message.
  405.  *
  406.  * Side effects:
  407.  *    Configuration information, such as text string, colors, font,
  408.  *    etc. get set for msgPtr;  old resources get freed, if there
  409.  *    were any.
  410.  *
  411.  *----------------------------------------------------------------------
  412.  */
  413.  
  414. static int
  415. ConfigureMessage(interp, msgPtr, argc, argv, flags)
  416.     Tcl_Interp *interp;        /* Used for error reporting. */
  417.     register Message *msgPtr;    /* Information about widget;  may or may
  418.                  * not already have values for some fields. */
  419.     int argc;            /* Number of valid entries in argv. */
  420.     char **argv;        /* Arguments. */
  421.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  422. {
  423.     /*
  424.      * Eliminate any existing trace on a variable monitored by the message.
  425.      */
  426.  
  427.     if (msgPtr->textVarName != NULL) {
  428.     Tcl_UntraceVar(interp, msgPtr->textVarName, 
  429.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  430.         MessageTextVarProc, (ClientData) msgPtr);
  431.     }
  432.  
  433.     if (Tk_ConfigureWidget(interp, msgPtr->tkwin, configSpecs,
  434.         argc, argv, (char *) msgPtr, flags) != TCL_OK) {
  435.     return TCL_ERROR;
  436.     }
  437.     
  438.     /*
  439.      * If the message is to display the value of a variable, then set up
  440.      * a trace on the variable's value, create the variable if it doesn't
  441.      * exist, and fetch its current value.
  442.      */
  443.  
  444.     if (msgPtr->textVarName != NULL) {
  445.     char *value;
  446.  
  447.     value = Tcl_GetVar(interp, msgPtr->textVarName, TCL_GLOBAL_ONLY);
  448.     if (value == NULL) {
  449.         Tcl_SetVar(interp, msgPtr->textVarName, msgPtr->string,
  450.             TCL_GLOBAL_ONLY);
  451.     } else {
  452.         if (msgPtr->string != NULL) {
  453.         ckfree(msgPtr->string);
  454.         }
  455.         msgPtr->string = strcpy(ckalloc(strlen(value) + 1), value);
  456.     }
  457.     Tcl_TraceVar(interp, msgPtr->textVarName,
  458.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  459.         MessageTextVarProc, (ClientData) msgPtr);
  460.     }
  461.  
  462.     /*
  463.      * A few other options need special processing, such as setting
  464.      * the background from a 3-D border or handling special defaults
  465.      * that couldn't be specified to Tk_ConfigureWidget.
  466.      */
  467.  
  468.     msgPtr->numChars = strlen(msgPtr->string);
  469.  
  470.     Tk_SetBackgroundFromBorder(msgPtr->tkwin, msgPtr->border);
  471.  
  472.     if (msgPtr->highlightWidth < 0) {
  473.     msgPtr->highlightWidth = 0;
  474.     }
  475.  
  476.     MessageWorldChanged((ClientData) msgPtr);
  477.     return TCL_OK;
  478. }
  479.  
  480. /*
  481.  *---------------------------------------------------------------------------
  482.  *
  483.  * MessageWorldChanged --
  484.  *
  485.  *      This procedure is called when the world has changed in some
  486.  *      way and the widget needs to recompute all its graphics contexts
  487.  *    and determine its new geometry.
  488.  *
  489.  * Results:
  490.  *      None.
  491.  *
  492.  * Side effects:
  493.  *      Message will be relayed out and redisplayed.
  494.  *
  495.  *---------------------------------------------------------------------------
  496.  */
  497.  
  498. static void
  499. MessageWorldChanged(instanceData)
  500.     ClientData instanceData;    /* Information about widget. */
  501. {
  502.     XGCValues gcValues;
  503.     GC gc;
  504.     Tk_FontMetrics fm;
  505.     Message *msgPtr;
  506.  
  507.     msgPtr = (Message *) instanceData;
  508.  
  509.     gcValues.font = Tk_FontId(msgPtr->tkfont);
  510.     gcValues.foreground = msgPtr->fgColorPtr->pixel;
  511.     gc = Tk_GetGC(msgPtr->tkwin, GCForeground | GCFont, &gcValues);
  512.     if (msgPtr->textGC != None) {
  513.     Tk_FreeGC(msgPtr->display, msgPtr->textGC);
  514.     }
  515.     msgPtr->textGC = gc;
  516.  
  517.     Tk_GetFontMetrics(msgPtr->tkfont, &fm);
  518.     if (msgPtr->padX < 0) {
  519.     msgPtr->padX = fm.ascent / 2;
  520.     }
  521.     if (msgPtr->padY == -1) {
  522.     msgPtr->padY = fm.ascent / 4;
  523.     }
  524.  
  525.     /*
  526.      * Recompute the desired geometry for the window, and arrange for
  527.      * the window to be redisplayed.
  528.      */
  529.  
  530.     ComputeMessageGeometry(msgPtr);
  531.     if ((msgPtr->tkwin != NULL) && Tk_IsMapped(msgPtr->tkwin)
  532.         && !(msgPtr->flags & REDRAW_PENDING)) {
  533.     Tcl_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
  534.     msgPtr->flags |= REDRAW_PENDING;
  535.     }
  536. }
  537.  
  538. /*
  539.  *--------------------------------------------------------------
  540.  *
  541.  * ComputeMessageGeometry --
  542.  *
  543.  *    Compute the desired geometry for a message window,
  544.  *    taking into account the desired aspect ratio for the
  545.  *    window.
  546.  *
  547.  * Results:
  548.  *    None.
  549.  *
  550.  * Side effects:
  551.  *    Tk_GeometryRequest is called to inform the geometry
  552.  *    manager of the desired geometry for this window.
  553.  *
  554.  *--------------------------------------------------------------
  555.  */
  556.  
  557. static void
  558. ComputeMessageGeometry(msgPtr)
  559.     register Message *msgPtr;    /* Information about window. */
  560. {
  561.     int width, inc, height;
  562.     int thisWidth, thisHeight, maxWidth;
  563.     int aspect, lowerBound, upperBound, inset;
  564.  
  565.     Tk_FreeTextLayout(msgPtr->textLayout);
  566.  
  567.     inset = msgPtr->borderWidth + msgPtr->highlightWidth;
  568.  
  569.     /*
  570.      * Compute acceptable bounds for the final aspect ratio.
  571.      */
  572.  
  573.     aspect = msgPtr->aspect/10;
  574.     if (aspect < 5) {
  575.     aspect = 5;
  576.     }
  577.     lowerBound = msgPtr->aspect - aspect;
  578.     upperBound = msgPtr->aspect + aspect;
  579.  
  580.     /*
  581.      * Do the computation in multiple passes:  start off with
  582.      * a very wide window, and compute its height.  Then change
  583.      * the width and try again.  Reduce the size of the change
  584.      * and iterate until dimensions are found that approximate
  585.      * the desired aspect ratio.  Or, if the user gave an explicit
  586.      * width then just use that.
  587.      */
  588.  
  589.     if (msgPtr->width > 0) {
  590.     width = msgPtr->width;
  591.     inc = 0;
  592.     } else {
  593.     width = WidthOfScreen(Tk_Screen(msgPtr->tkwin))/2;
  594.     inc = width/2;
  595.     }
  596.  
  597.     for ( ; ; inc /= 2) {
  598.     msgPtr->textLayout = Tk_ComputeTextLayout(msgPtr->tkfont,
  599.         msgPtr->string, msgPtr->numChars, width, msgPtr->justify,
  600.         0, &thisWidth, &thisHeight);
  601.     maxWidth = thisWidth + 2 * (inset + msgPtr->padX);
  602.     height = thisHeight + 2 * (inset + msgPtr->padY);
  603.  
  604.     if (inc <= 2) {
  605.         break;
  606.     }
  607.     aspect = (100 * maxWidth) / height;
  608.  
  609.     if (aspect < lowerBound) {
  610.         width += inc;
  611.     } else if (aspect > upperBound) {
  612.         width -= inc;
  613.     } else {
  614.         break;
  615.     }
  616.     Tk_FreeTextLayout(msgPtr->textLayout);
  617.     }
  618.     msgPtr->msgWidth = thisWidth;
  619.     msgPtr->msgHeight = thisHeight;
  620.     Tk_GeometryRequest(msgPtr->tkwin, maxWidth, height);
  621.     Tk_SetInternalBorder(msgPtr->tkwin, inset);
  622. }
  623.  
  624. /*
  625.  *--------------------------------------------------------------
  626.  *
  627.  * DisplayMessage --
  628.  *
  629.  *    This procedure redraws the contents of a message window.
  630.  *
  631.  * Results:
  632.  *    None.
  633.  *
  634.  * Side effects:
  635.  *    Information appears on the screen.
  636.  *
  637.  *--------------------------------------------------------------
  638.  */
  639.  
  640. static void
  641. DisplayMessage(clientData)
  642.     ClientData clientData;    /* Information about window. */
  643. {
  644.     register Message *msgPtr = (Message *) clientData;
  645.     register Tk_Window tkwin = msgPtr->tkwin;
  646.     int x, y;
  647.  
  648.     msgPtr->flags &= ~REDRAW_PENDING;
  649.     if ((msgPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  650.     return;
  651.     }
  652.     Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), msgPtr->border, 0, 0,
  653.         Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  654.  
  655.     /*
  656.      * Compute starting y-location for message based on message size
  657.      * and anchor option.
  658.      */
  659.  
  660.     TkComputeAnchor(msgPtr->anchor, tkwin, msgPtr->padX, msgPtr->padY,
  661.         msgPtr->msgWidth, msgPtr->msgHeight, &x, &y);
  662.     Tk_DrawTextLayout(Tk_Display(tkwin), Tk_WindowId(tkwin), msgPtr->textGC,
  663.         msgPtr->textLayout, x, y, 0, -1);
  664.  
  665.     if (msgPtr->relief != TK_RELIEF_FLAT) {
  666.     Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), msgPtr->border,
  667.         msgPtr->highlightWidth, msgPtr->highlightWidth,
  668.         Tk_Width(tkwin) - 2*msgPtr->highlightWidth,
  669.         Tk_Height(tkwin) - 2*msgPtr->highlightWidth,
  670.         msgPtr->borderWidth, msgPtr->relief);
  671.     }
  672.     if (msgPtr->highlightWidth != 0) {
  673.     GC gc;
  674.  
  675.     if (msgPtr->flags & GOT_FOCUS) {
  676.         gc = Tk_GCForColor(msgPtr->highlightColorPtr, Tk_WindowId(tkwin));
  677.     } else {
  678.         gc = Tk_GCForColor(msgPtr->highlightBgColorPtr, Tk_WindowId(tkwin));
  679.     }
  680.     Tk_DrawFocusHighlight(tkwin, gc, msgPtr->highlightWidth,
  681.         Tk_WindowId(tkwin));
  682.     }
  683. }
  684.  
  685. /*
  686.  *--------------------------------------------------------------
  687.  *
  688.  * MessageEventProc --
  689.  *
  690.  *    This procedure is invoked by the Tk dispatcher for various
  691.  *    events on messages.
  692.  *
  693.  * Results:
  694.  *    None.
  695.  *
  696.  * Side effects:
  697.  *    When the window gets deleted, internal structures get
  698.  *    cleaned up.  When it gets exposed, it is redisplayed.
  699.  *
  700.  *--------------------------------------------------------------
  701.  */
  702.  
  703. static void
  704. MessageEventProc(clientData, eventPtr)
  705.     ClientData clientData;    /* Information about window. */
  706.     XEvent *eventPtr;        /* Information about event. */
  707. {
  708.     Message *msgPtr = (Message *) clientData;
  709.  
  710.     if (((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0))
  711.         || (eventPtr->type == ConfigureNotify)) {
  712.     goto redraw;
  713.     } else if (eventPtr->type == DestroyNotify) {
  714.     if (msgPtr->tkwin != NULL) {
  715.         msgPtr->tkwin = NULL;
  716.         Tcl_DeleteCommandFromToken(msgPtr->interp, msgPtr->widgetCmd);
  717.     }
  718.     if (msgPtr->flags & REDRAW_PENDING) {
  719.         Tcl_CancelIdleCall(DisplayMessage, (ClientData) msgPtr);
  720.     }
  721.     Tcl_EventuallyFree((ClientData) msgPtr, DestroyMessage);
  722.     } else if (eventPtr->type == FocusIn) {
  723.     if (eventPtr->xfocus.detail != NotifyInferior) {
  724.         msgPtr->flags |= GOT_FOCUS;
  725.         if (msgPtr->highlightWidth > 0) {
  726.         goto redraw;
  727.         }
  728.     }
  729.     } else if (eventPtr->type == FocusOut) {
  730.     if (eventPtr->xfocus.detail != NotifyInferior) {
  731.         msgPtr->flags &= ~GOT_FOCUS;
  732.         if (msgPtr->highlightWidth > 0) {
  733.         goto redraw;
  734.         }
  735.     }
  736.     }
  737.     return;
  738.  
  739.     redraw:
  740.     if ((msgPtr->tkwin != NULL) && !(msgPtr->flags & REDRAW_PENDING)) {
  741.     Tcl_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
  742.     msgPtr->flags |= REDRAW_PENDING;
  743.     }
  744. }
  745.  
  746. /*
  747.  *----------------------------------------------------------------------
  748.  *
  749.  * MessageCmdDeletedProc --
  750.  *
  751.  *    This procedure is invoked when a widget command is deleted.  If
  752.  *    the widget isn't already in the process of being destroyed,
  753.  *    this command destroys it.
  754.  *
  755.  * Results:
  756.  *    None.
  757.  *
  758.  * Side effects:
  759.  *    The widget is destroyed.
  760.  *
  761.  *----------------------------------------------------------------------
  762.  */
  763.  
  764. static void
  765. MessageCmdDeletedProc(clientData)
  766.     ClientData clientData;    /* Pointer to widget record for widget. */
  767. {
  768.     Message *msgPtr = (Message *) clientData;
  769.     Tk_Window tkwin = msgPtr->tkwin;
  770.  
  771.     /*
  772.      * This procedure could be invoked either because the window was
  773.      * destroyed and the command was then deleted (in which case tkwin
  774.      * is NULL) or because the command was deleted, and then this procedure
  775.      * destroys the widget.
  776.      */
  777.  
  778.     if (tkwin != NULL) {
  779.     msgPtr->tkwin = NULL;
  780.     Tk_DestroyWindow(tkwin);
  781.     }
  782. }
  783.  
  784. /*
  785.  *--------------------------------------------------------------
  786.  *
  787.  * MessageTextVarProc --
  788.  *
  789.  *    This procedure is invoked when someone changes the variable
  790.  *    whose contents are to be displayed in a message.
  791.  *
  792.  * Results:
  793.  *    NULL is always returned.
  794.  *
  795.  * Side effects:
  796.  *    The text displayed in the message will change to match the
  797.  *    variable.
  798.  *
  799.  *--------------------------------------------------------------
  800.  */
  801.  
  802.     /* ARGSUSED */
  803. static char *
  804. MessageTextVarProc(clientData, interp, name1, name2, flags)
  805.     ClientData clientData;    /* Information about message. */
  806.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  807.     char *name1;        /* Name of variable. */
  808.     char *name2;        /* Second part of variable name. */
  809.     int flags;            /* Information about what happened. */
  810. {
  811.     register Message *msgPtr = (Message *) clientData;
  812.     char *value;
  813.  
  814.     /*
  815.      * If the variable is unset, then immediately recreate it unless
  816.      * the whole interpreter is going away.
  817.      */
  818.  
  819.     if (flags & TCL_TRACE_UNSETS) {
  820.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  821.         Tcl_SetVar(interp, msgPtr->textVarName, msgPtr->string,
  822.             TCL_GLOBAL_ONLY);
  823.         Tcl_TraceVar(interp, msgPtr->textVarName,
  824.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  825.             MessageTextVarProc, clientData);
  826.     }
  827.     return (char *) NULL;
  828.     }
  829.  
  830.     value = Tcl_GetVar(interp, msgPtr->textVarName, TCL_GLOBAL_ONLY);
  831.     if (value == NULL) {
  832.     value = "";
  833.     }
  834.     if (msgPtr->string != NULL) {
  835.     ckfree(msgPtr->string);
  836.     }
  837.     msgPtr->numChars = strlen(value);
  838.     msgPtr->string = (char *) ckalloc((unsigned) (msgPtr->numChars + 1));
  839.     strcpy(msgPtr->string, value);
  840.     ComputeMessageGeometry(msgPtr);
  841.  
  842.     if ((msgPtr->tkwin != NULL) && Tk_IsMapped(msgPtr->tkwin)
  843.         && !(msgPtr->flags & REDRAW_PENDING)) {
  844.     Tcl_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
  845.     msgPtr->flags |= REDRAW_PENDING;
  846.     }
  847.     return (char *) NULL;
  848. }
  849.